#ifndef prs_CalcExpr_H
#define prs_CalcExpr_H

#include "sntText.h"
#include "sntTextDouble.h"
#include "prs_CalcNodes.h"
#include "prs_FrmDict.h"
#include "prs_FrmValue.h"

namespace prs {

///////////////////////////////////////////////////////////////////////////////

struct TNodeSOper : public TNodeCalc
{
  DECLARE_ClientNode()
  
  TNodeSOper(): Child(this) {}
  
  TAttrPointerNodeOwner<attrName_Child,TNodeCalc*> Child;
  
  void       addChild(TNodeCalc *child) { Child.SetValue(child); }
  TNodeCalc *getChild()                 { return Child.GetValue(); }
  
private:
  svl_Except;
};

struct TNodeSOper_Minus : public TNodeSOper
{
  DECLARE_ClientNode()
  
  virtual double Calc() { return -getChild()->Calc();}
};
struct TNodeSOper_Plus : public TNodeSOper
{
  DECLARE_ClientNode()
  
  virtual double Calc() { return +getChild()->Calc();}
};
struct TNodeSOper_Not : public TNodeSOper
{
  DECLARE_ClientNode()
  
  virtual double Calc() { return !getChild()->Calc();}
};

///////////////////////////////////////////////////////////////////////////////

struct TNodeDOper : public TNodeCalc
{
  DECLARE_ClientNode()
  
  TNodeDOper(): LeftChild(this),RightChild(this) {}
	
  TAttrPointerNodeOwner<attrName_LChild,TNodeCalc*> LeftChild; 
  TAttrPointerNodeOwner<attrName_RChild,TNodeCalc*> RightChild; 
  
  void       addLChild(TNodeCalc *lChild) { LeftChild.SetValue(lChild); }
  TNodeCalc *getLChild()                  { return LeftChild.GetValue(); }
  //
  void       addRChild(TNodeCalc *rChild) { RightChild.SetValue(rChild); }
  TNodeCalc *getRChild()                  { return RightChild.GetValue(); }
  //

private:
  svl_Except;
};

struct TNodeDOper_Mul : public TNodeDOper
{
  DECLARE_ClientNode()
  
  virtual double Calc() { return getLChild()->Calc() * getRChild()->Calc();}
};
struct TNodeDOper_Div : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() / getRChild()->Calc();}
};
struct TNodeDOper_Add : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() + getRChild()->Calc();}
};
struct TNodeDOper_Sub : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() - getRChild()->Calc();}
};

struct TNodeDOper_LogAnd : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() && getRChild()->Calc();}
};
struct TNodeDOper_LogOr : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() || getRChild()->Calc();}
};

struct TNodeDOper_Less : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() <  getRChild()->Calc();}
};
struct TNodeDOper_LessEqual : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() <= getRChild()->Calc();}
};
struct TNodeDOper_Greater : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() >  getRChild()->Calc();}
};
struct TNodeDOper_GreaterEqual : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() >= getRChild()->Calc();}
};
struct TNodeDOper_Equal : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() == getRChild()->Calc();}
};
struct TNodeDOper_NotEqual : public TNodeDOper
{
  DECLARE_ClientNode()
  virtual double Calc() { return getLChild()->Calc() != getRChild()->Calc();}
};
                                                  
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////  

struct TRuleExpr : public snt::TRule
{
  TRuleExpr(snt::text::TContext &Context,svl::TVariant &VParent,snt::TRule &RParam);

private:
  static void FSOper(snt::TParams &Params);
  static void FDOper(snt::TParams &Params);
  static void FNewExpr(snt::TParams &Params);
  static void FBegParam(snt::TParams &Params);
  static void FEndParam(snt::TParams &Params);
  //
  svl::TVariant &Op(const TNodeCalc &V);

private:
  svl_Except;
  snt::TRule  RFactor;
  snt::TRule  RMulDiv,RAddSub,RLessGreater,REquality,RLogAnd,RExpr;
  snt::TAlter AMulDiv,AAddSub,ALessGreater,AEquality,ALogAnd,AExpr;
  //
  svl::TVariant &vParent,vChild,vLeft,vThis;

  struct TOpItem : public svl::TArray_Object
  {
    svl::TVariant V; 
    TNodeCalc *Node;
    //
    TOpItem(TNodeCalc &V) : Node(dynamic_cast<TNodeCalc*>(V.New())),V(this) {}
   ~TOpItem() { delete Node;}
  };
public:

  struct TTempNode : public TNodeCalc
  {
	DECLARE_ClientNode()	
	TTempNode(): OldChildren(this) {}
	//
    TAttrNodeArray<attrName_OldChildren,TNodeArray> OldChildren;
	TNodeCalc *getChild() { return dynamic_cast<TNodeCalc*>(OldChildren.Array.Count() ? &OldChildren.Array.Get(0) : NULL); }
  };

private:
  prs::TNodeArray tempNodes; 
  svl::TArray opArray;
};

////////////////////////////////////////////////////////////////////////////////
struct TRuleExprParam : public snt::TRule{
	
	TRuleExprParam( snt::text::TContext &Context
				  , svl::TVariant &Parent );
  
	svl::TVariant &parent;

	bool FVar(snt::text::TIdent::TFuncClosureDoEntryParams &Params);
	bool FInt(snt::text::TNumber::TFuncClosureDoEntryParams &Params);
	bool FDbl(sntText::TDouble::TFuncClosureDoEntryParams &Params);
};

struct TNodeExpr : public TNodeCalc
{
  DECLARE_ClientNode()
  
  TNodeExpr(): Child(this) {}
  
  TAttrPointerNodeOwner<attrName_Child,TNodeCalc*> Child;
  
  void       addChild(TNodeCalc *child) { Child.SetValue(child); }
  TNodeCalc *getChild()                 { return Child.GetValue(); }
	
  virtual double Calc() { return getChild() ? getChild()->Calc() : 0;}
};



struct TNodeExprNumb : public prs::TNodeCalc{
	
	DECLARE_ClientNode()
  
	TNodeExprNumb(): Value(this) { Value = 0;}
	virtual double Calc() { return Value; }
	
	TAttrInteger<attrName_Value> Value;
};

struct TNodeExprDbl : public prs::TNodeCalc{
  
	DECLARE_ClientNode()
  
	TNodeExprDbl():Value(this) { Value = 0.0;}
	virtual double Calc() { return Value; }
	
	TAttrDouble<attrName_Value> Value;
};

struct TNodeExprVar : public prs::TNodeCalc{
	
	DECLARE_ClientNode()
  
	TNodeExprVar(): Id(this), Link(this) {}
	virtual double Calc();
	
	TAttrString<attrName_Id> Id;
	TAttrPointer<TFrmDict::attrName_Link,TFrmValue*> Link;
};

}

#endif